/*
 * MozartToCorsoBridge.java
 *
 * Created on 06. Juni 2007, 07:42
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package sbc.jms;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.jms.JMSException;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import org.xvsm.api.core.*;
import org.xvsm.client.ClientCapi;
import org.xvsm.api.core.notification.*;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.xvsm.ms.exceptions.DoubleCrefException;
import org.xvsm.ms.exceptions.FatalException;
import org.xvsm.ms.exceptions.TimeoutExpiredException;
import sbc.fabrik.model.Feature;
import sbc.fabrik.model.Platine;
import sbc.fabrik.model.Sensor;
import sbc.util.Util;

/**
 *
 * @author mustermark
 */
public class MozartToJMSBridge implements Bridge<List<Entry>>, Runnable {
    
    private static Capi capi;
    private static ContainerRef cref;
    Context jndiContext = null;
    QueueConnectionFactory  queueConnectionFactory = null;
    QueueConnection queueConnection = null;
    Queue queue = null;
    QueueSession session = null;
    QueueSender publisher = null;
    
    /**
     * Creates a new instance of MozartToCorsoBridge
     */
    public MozartToJMSBridge(Capi c) throws DoubleCrefException, FatalException {
        super();
        capi = c;
        cref = capi.createContainer(CoordinationTypes.FIFO);
        
    }
    
    public MozartToJMSBridge() throws DoubleCrefException, FatalException {
        super();
    }
    
    
    public void disestablish() {
    }
    public void establish() {
        try {
            Properties properties = new Properties();
            properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
            properties.put(Context.URL_PKG_PREFIXES, "org.jnp.interfaces");
            properties.put(Context.PROVIDER_URL, "localhost");
            
            
            
            jndiContext = new InitialContext(properties);
        /*
         * Look up connection factory and topic.  If either does
         * not exist, exit.
         */
            queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup("ConnectionFactory");
            queue = (Queue) jndiContext.lookup("queue/sbc");
            
            queueConnection = queueConnectionFactory.createQueueConnection();
            session = (QueueSession) queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
            publisher = session.createSender(queue);
            
            
        } catch (NamingException e) {
            System.out.println("JNDI lookup failed: " + e.toString());
            e.printStackTrace();
            System.exit(1);
        } catch (JMSException e) {
            System.out.println("JMS: " + e.toString());
            e.printStackTrace();
            System.exit(1);
        }
    }
    
    public void route(List<Entry> entries) throws BridgeException{
        try {
            for(Entry en : entries) {
                if(en.getType() == ValueTypes.TUPLE) {
                    route((Tuple)en);
                    
                }
                
            }
        } catch (Exception e) {
            System.out.println(e);
        }
    }
    private void route(Tuple t) throws BridgeException, JMSException {
        List<Entry> elements = t.getValue();
        
        String type = ((String)elements.get(0).getValue()).toUpperCase();
        //System.out.println("UnitType is " + type);
        if(type.equals("BOARD")) {
            Platine platine = new Platine();
            platine.setPid((String)elements.get(1).getValue());
            ObjectMessage message = session.createObjectMessage();
            message.setObject(platine);
            message.setStringProperty("ModulType", platine.getClass().getCanonicalName());
            message.setBooleanProperty("Completed", false);
            System.out.println("JMS: " + message.getObject().toString());
            publisher.send(message);
            
        } else if(type.equals("SENSOR")) {
            Sensor sensor = new Sensor();
            sensor.setSid((String)elements.get(1).getValue());
            
            ObjectMessage message = session.createObjectMessage();
            //EnumSet<Feature> features = EnumSet.noneOf(Feature.class);
            for(int i = 2; i < elements.size(); i++) {
                sensor.addFeature(Feature.valueOf(((String)elements.get(i).getValue()).toUpperCase()));
                message.setBooleanProperty(Feature.valueOf(((String)elements.get(i).getValue()).toUpperCase()).name(), false);
            }
            message.setObject(sensor);
            message.setStringProperty("ModulType", sensor.getClass().getCanonicalName());
            System.out.println("JMS: " + message.getObject().toString());
            publisher.send(message);
        }
        
        
        
        
    }
    
    public void run() {
        
        try {
            //final ContainerRef cref = Util.getOrCreateNamedContainer(capi, "DefaultContainer", CoordinationTypes.FIFO);
            capi.createNotification(cref, -1, NotificationTarget.WRITE.code(), NotificationMode.INFINITE, true, new NotificationListener() {
                public void sendNotification(NotificationID id) {
                    //System.out.println("sending " + id);
                    try {
                        route(capi.take(cref));
                    } catch(Exception ex) {
                        System.out.println("Couldn't route...");
                    }
                    
                }
                
                public void sendNotification(NotificationID id, java.util.List<Entry> entries) {
                    //System.out.println("sending " + id + " with " + entries);
                    //route(entries);
                    try {
                        String type = "";
                        Entry en = entries.get(0);
                        if(en.getType() == ValueTypes.TUPLE) {
                            Tuple tuple = (Tuple) entries.get(0);
                            List<Entry> elements = tuple.getValue();
                            type = ((String)elements.get(0).getValue()).toUpperCase();
                        }
                        //System.out.println(type);
                        
                        if (!type.equals("UNIT")) {
                            route(capi.take(cref));
                        } else {
                          //  System.out.println("UNIT");
                        }
                    } catch(Exception ex) {
                        System.out.println("Couldn't route " + entries);
                        System.out.println(ex);
                    }
                }
                
                
            });
        } catch(Exception ex) { ex.printStackTrace(); }
        
    }
    
    // }
    
    public static void main(String[] args) {
        try {
            Logger.getLogger("org.xvsm.client").setLevel(Level.FATAL);
            
            if (args.length != 1) {
                System.out.println("URL needed!");
                System.out.println("Usage: java -cp <classpath> org.xvsm.example.Fifo http://localhost:8084/xvsm/xvsm\n using embeded core");
                // use embeded xvsm core
                capi = CapiFactory.createCapi();
                //capi = new ClientCapi("http://localhost:8084/xvsm-0.8.3");
            } else {
                // Get an instance of the capi and initialize it.
                // Capi capi = CapiFactory.createCapi();
                capi = new ClientCapi(args[0]);
            }
            capi.init(false);
            cref = Util.getOrCreateNamedContainer(capi, "DefaultContainer", CoordinationTypes.FIFO);
            //cref = capi.createContainer(CoordinationTypes.FIFO);
            
            MozartToJMSBridge bridge = new MozartToJMSBridge();
            bridge.establish();
            bridge.run();
            //bridge.addClient(Factory.DEFAULT_FACTORY_NAME);
            
            //UI ui = new UI("http://localhost:8084/xvsm-0.8.3");
            
            
            
            //cli();
            infinitewait();
            
            
            /* starting cli shell */
            
        } catch (Exception  e) {
            e.printStackTrace();
            
        }
    }
    public static void cli() {
        try {
            BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
            
            //final ContainerRef cref = Util.getOrCreateNamedContainer(capi, "DefaultContainer", CoordinationTypes.FIFO);
            
            System.out.println("\nFabrikShell started at " + (new Date()).toString());
            System.out.println("Type '\\h' or 'help' for help.\n");
            boolean run = true;
            while(run) {
                
                System.out.println("\nFabrik\\> ");
                System.out.flush();
                String command = input.readLine().trim();
                //String command = input.readLine();
                //System.out.println("command\\> " + command);
                if(command.equalsIgnoreCase("quit") || command.equalsIgnoreCase("\\q")) {
                    System.out.println("\nGood Bye!\n");
                    //dirty...
                    //System.exit(0);
                    
                    run = false;
                    
                } else if(command.startsWith("help") || command.startsWith("\\h")) {
                    help();
                    continue;
                } else if(command.startsWith("sensor") || command.startsWith("\\s")) {
                    StringTokenizer paramTokenizer = new StringTokenizer(command, " ");
                    paramTokenizer.nextToken();
                    
                    String sid =  (String) paramTokenizer.nextToken();
                    Sensor sensor = new Sensor(sid);
                    
                    while (paramTokenizer.hasMoreTokens()) {
                        String featureName = (String)paramTokenizer.nextToken();
                        try{
                            
                            sensor.addFeature(Feature.valueOf(featureName));
                            
                        } catch (IllegalArgumentException iae) {
                            System.out.println(featureName + ": unknown feature");
                            System.out.println("valid values are:");
                            for(Feature f: Feature.values()) {
                                System.out.println(f.name() + "\t(" + f.getLabel() + ")");
                            }
                        }
                    }
                    
                    
                    
                    Entry classEntry = Entry.Factory.newInstance(ValueTypes.STRING_UTF8, "SENSOR");
                    Entry idEntry = Entry.Factory.newInstance(ValueTypes.STRING_UTF8, sensor.getSid());
                    
                    Tuple tuple = new Tuple();
                    tuple.addEntry(classEntry);
                    tuple.addEntry(idEntry);
                    
                    for (Feature f: sensor.getFeatures()) {
                        Entry featureEntry = Entry.Factory.newInstance(ValueTypes.STRING_UTF8, f.name());
                        tuple.addEntry(featureEntry);
                    }
                    
                    List<Entry> entries = new LinkedList<Entry>();
                    entries.add(tuple);
                    
                    // Write the list of Entries into the created container.
                    capi.write(cref, entries);
                    
                    
                    System.out.println("Wrote:" + sensor);
                }
                
                else if(command.startsWith("platine") || command.startsWith("\\p")) {
                    StringTokenizer paramTokenizer = new StringTokenizer(command, " ");
                    paramTokenizer.nextToken();
                    if(!paramTokenizer.hasMoreTokens()) {
                        help();
                        continue;
                    }
                    
                    String pid =  (String)paramTokenizer.nextToken();
                    
                    Platine p = new Platine(pid);
                    
                    Entry a0 = Entry.Factory.newInstance(ValueTypes.STRING_UTF8, "BOARD");
                    Entry a1 = Entry.Factory.newInstance(ValueTypes.STRING_UTF8, p.getPid());
                    Tuple t1 = new Tuple();
                    t1.addEntry(a0);
                    t1.addEntry(a1);
                    List<Entry> entries = new LinkedList<Entry>();
                    entries.add(t1);
                    
                    // Write the list of Entries into the created container.
                    capi.write(cref, entries);
                    
                    
                    System.out.println("Wrote:" + p);
                }
                
                else if(command.startsWith("read") || command.startsWith("\\r")) {
                    // Read and delete the elements from the space.
                    boolean empty = false;
                    while (!empty) {
                        try {
                            List<Entry> read = capi.take(cref,1);
                            // The first element in the list is the tuple.
                            Tuple t = (Tuple) read.get(0);
                            // Get the entries of the tuple
                            List<Entry> elements = t.getValue();
                            
                            for (Entry e: elements) {
                                System.out.print(e.getValue() + " ");
                            }
                            System.out.println();
                        } catch (TimeoutExpiredException tee) {
                            empty = true;
                            System.out.println("Space Empty!");
                        }
                    }
                }
            }
        } catch (Exception  e) {
            e.printStackTrace();
            
        }
        
    }
    
    
    public static void help() {
        System.out.println("\n*** FabrikShell Commands ***\n");
        System.out.println("\\s, sensor <id> <feature> [[<feature>] [<feature>]]: new sensor.");
        System.out.println("\\p, platine <id>: new platine.");
        System.out.println("\\r, read: read space.");
        System.out.println("\\q, quit: quit program.");
        System.out.println("\\h, help: this message.\n");
        System.out.println("****************************\n");
    }
    
    public static void infinitewait() {
        InputStreamReader       inputStreamReader = null;
        char                    answer = '\0';
        
        System.out.println("To end program, enter Q or q, then <return>");
        inputStreamReader = new InputStreamReader(System.in);
        while (!((answer == 'q') || (answer == 'Q'))) {
            
            try {
                // millisecs
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
            }
        }
        try {
            answer = (char) inputStreamReader.read();
        } catch (IOException e) {
            System.out.println("I/O exception: "
                    + e.toString());
        }
    }
    
}
